' Stephen Toub
' Coded and published in January 2007 issue of MSDN Magazine 
' http://msdn.microsoft.com/msdnmag/issues/07/01/PreviewHandlers/default.aspx


Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Text
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports java.io
Imports java.util
Imports java.util.zip
Imports System.Collections
Imports System.Collections.Generic
Imports C4F.DevKit.PreviewHandler.PreviewHandlerFramework

Namespace C4F.DevKit.PreviewHandler.PreviewHandlers
    <PreviewHandler("MSDN Magazine ZIP Preview Handler", ".zip;.gadget", "{c0a64ec6-729b-442d-88ce-d76a9fc69e44}"), ProgId("C4F.DevKit.PreviewHandler.PreviewHandlers.ZipPreviewHandler"), Guid("853f35e3-bd13-417b-b859-1df25be6c834"), ClassInterface(ClassInterfaceType.None), ComVisible(True)> _
    Public NotInheritable Class ZipPreviewHandler
        Inherits FileBasedPreviewHandler
        Protected Overrides Function CreatePreviewHandlerControl() As PreviewHandlerControl
            Return New ZipPreviewHandlerControl()
        End Function
        Private NotInheritable Class ZipPreviewHandlerControl
            Inherits FileBasedPreviewHandlerControl
            Private _file As FileInfo
            Public Overrides Sub Load(ByVal file As FileInfo)
                _file = file
                Dim folderView As TreeView = New TreeView()
                folderView.ImageList = _iconProvider.Icons
                folderView.Dock = DockStyle.Fill
                folderView.FullRowSelect = True
                folderView.ShowLines = True
                folderView.ShowPlusMinus = True
                folderView.ShowRootLines = True
                folderView.LabelEdit = False
                folderView.BorderStyle = BorderStyle.None
                folderView.TreeViewNodeSorter = New NodeSorter()

                Dim filenames As List(Of String) = New List(Of String)()
                Dim zip As ZipFile = New ZipFile(file.FullName)
                Dim entryEnum As Enumeration = zip.entries()
                Do While entryEnum.hasMoreElements()
                    Dim entry As ZipEntry = CType(entryEnum.nextElement(), ZipEntry)
                    If (Not entry.isDirectory()) Then
                        filenames.Add(entry.getName())
                    End If
                Loop
                zip.close()

                Dim root As TreeNode = ConvertFilenamesToTreeRoot(file.Name, filenames)
                folderView.Nodes.Add(root)
                root.Expand()

                AddHandler folderView.DoubleClick, AddressOf anonDoubleClick

                Controls.Add(folderView)
            End Sub
            Private Sub anonDoubleClick(ByVal sender As Object, ByVal e As EventArgs)
                Dim folderView As TreeView = CType(sender, TreeView)
                If Not folderView.SelectedNode Is Nothing AndAlso Not TryCast(folderView.SelectedNode.Tag, String) Is Nothing Then
                    Dim selectedNode As TreeNode = folderView.SelectedNode
                    Dim zipToOpen As ZipFile = New ZipFile(_file.FullName)
                    Dim tempPath As String = ExtractZipEntryToFile(CStr(selectedNode.Tag), zipToOpen)
                    zipToOpen.close()
                    Process.Start(tempPath)
                End If
            End Sub

            Private Class NodeSorter
                Implements IComparer
                Private Function IComparer_Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
                    Dim node1 As TreeNode = CType(x, TreeNode)
                    Dim node2 As TreeNode = CType(y, TreeNode)

                    Dim tag1 As String = TryCast(node1.Tag, String)
                    Dim tag2 As String = TryCast(node2.Tag, String)

                    If tag1 Is Nothing AndAlso tag2 Is Nothing Then
                        Return StrCmpLogicalW(node1.Text, node2.Text)
                    ElseIf tag1 = tag2 Then
                        Return 0
                    ElseIf tag1 Is Nothing Then
                        Return -1
                    ElseIf tag2 Is Nothing Then
                        Return 1
                    Else
                        Return StrCmpLogicalW(tag1, tag2)
                    End If
                End Function

                <DllImport("shlwapi.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True)> _
                Private Shared Function StrCmpLogicalW(ByVal strA As String, ByVal strB As String) As Integer
                End Function
            End Class

            Private Function ConvertFilenamesToTreeRoot(ByVal rootName As String, ByVal names As IEnumerable(Of String)) As TreeNode
                Dim rootNode As TreeNode = New TreeNode(rootName)
                rootNode.ImageIndex = _iconProvider.FolderIndex
                For Each name As String In names
                    Dim pathParts As String() = name.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
                    Dim parent As TreeNode = rootNode
                    For i As Integer = 0 To pathParts.Length - 1
                        Dim pathPart As String = pathParts(i)
                        Dim foundNodes As TreeNode() = parent.Nodes.Find(pathPart, False)
                        Dim partNode As TreeNode
                        If foundNodes.Length = 0 OrElse i = pathParts.Length - 1 Then
                            partNode = New TreeNode(pathPart)
                            partNode.Name = pathPart
                            parent.Nodes.Add(partNode)
                            If i = pathParts.Length - 1 Then
                                partNode.ImageIndex = _iconProvider.GetIconIndexForFile(name)
                                partNode.Tag = name
                            Else
                                partNode.ImageIndex = _iconProvider.FolderIndex
                            End If
                        Else
                            partNode = foundNodes(0)
                        End If
                        partNode.SelectedImageIndex = partNode.ImageIndex
                        parent = partNode
                    Next i
                Next name
                Return rootNode
            End Function

            Private Shared Function ExtractZipEntryToFile(ByVal entryName As String, ByVal zip As ZipFile) As String
                Dim entry As ZipEntry = zip.getEntry(entryName)

                Dim fromFile As InputStream = zip.getInputStream(entry)
                Dim tempPath As String = Path.GetTempPath() + Path.GetFileName(entry.getName())
                Dim [to] As OutputStream = New FileOutputStream(tempPath)

                Dim buffer As SByte() = New SByte(&H1000 - 1) {}
                Dim amountRead As Integer
                Do While (amountRead = fromFile.read(buffer, 0, buffer.Length)) > 0
                    [to].write(buffer, 0, amountRead)
                Loop

                fromFile.close()
                [to].close()

                Return tempPath
            End Function

            Private _iconProvider As FileTypeIconProvider = New FileTypeIconProvider()

            Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
                If disposing AndAlso Not _iconProvider Is Nothing Then
                    _iconProvider.Dispose()
                    _iconProvider = Nothing
                End If
                MyBase.Dispose(disposing)
            End Sub
        End Class
    End Class
End Namespace